/*
 * MainFrame.java
 *
 * Copyright 2006 Sun Microsystems, Inc. ALL RIGHTS RESERVED Use of
 * this software is authorized pursuant to the terms of the license
 * found at http://developers.sun.com/berkeley_license.html.
 *
 */

package bill.study.swing.swingworker;

import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Properties;
import javax.swing.DefaultListModel;
import javax.swing.JList;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.UIManager;


/**
 * MainFrame is the launch point for the demo. It provides the
 * main method that runs on the initial thread, and it provides
 * a JFrame subclass that runs on the Swing event dispatch thread.
 * 
 * 
 * @author John O'Conner
 */
public class MainFrame extends javax.swing.JFrame {
    
    /**
     * Creates new form MainFrame
     */
    public MainFrame() {
        initComponents();
    }
    
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
    private void initComponents() {
        lblSearch = new javax.swing.JLabel();
        txtSearch = new javax.swing.JTextField();
        lblImageList = new javax.swing.JLabel();
        scrollImageList = new javax.swing.JScrollPane();
        listModel = new DefaultListModel();
        renderer = new ImageInfoRenderer();
        listImages = new JList(listModel);
        listImages.setCellRenderer(renderer);
        lblSelectedImage = new javax.swing.JLabel();
        lblImage = new javax.swing.JLabel();
        progressMatchedImages = new javax.swing.JProgressBar();
        listenerMatchedImages = new ProgressListener(progressMatchedImages);
        progressSelectedImage = new javax.swing.JProgressBar();
        listenerSelectedImage = new ProgressListener(progressSelectedImage);

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Image Search");
        lblSearch.setText("Search");

        txtSearch.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                txtSearchKeyPressed(evt);
            }
        });

        lblImageList.setText("Matched Images");

        listImages.setModel(listModel);
        listImages.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
        listImages.setCellRenderer(renderer);
        listImages.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
            public void valueChanged(javax.swing.event.ListSelectionEvent evt) {
                listImagesValueChanged(evt);
            }
        });

        scrollImageList.setViewportView(listImages);

        lblSelectedImage.setText("Selected Image");

        lblImage.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(204, 204, 204)));
        lblImage.setFocusable(false);
        lblImage.setMaximumSize(new java.awt.Dimension(500, 500));
        lblImage.setMinimumSize(new java.awt.Dimension(250, 250));
        lblImage.setOpaque(true);
        lblImage.setPreferredSize(new java.awt.Dimension(500, 250));

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(lblImage, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 462, Short.MAX_VALUE)
                    .addComponent(scrollImageList, javax.swing.GroupLayout.DEFAULT_SIZE, 462, Short.MAX_VALUE)
                    .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(lblImageList)
                            .addComponent(lblSelectedImage))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(progressMatchedImages, javax.swing.GroupLayout.DEFAULT_SIZE, 350, Short.MAX_VALUE)
                            .addComponent(progressSelectedImage, javax.swing.GroupLayout.DEFAULT_SIZE, 350, Short.MAX_VALUE)))
                    .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
                        .addComponent(lblSearch)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(txtSearch, javax.swing.GroupLayout.DEFAULT_SIZE, 411, Short.MAX_VALUE)))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(lblSearch)
                    .addComponent(txtSearch, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(lblImageList)
                    .addComponent(progressMatchedImages, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(scrollImageList, javax.swing.GroupLayout.PREFERRED_SIZE, 235, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(lblSelectedImage)
                    .addComponent(progressSelectedImage, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(lblImage, javax.swing.GroupLayout.DEFAULT_SIZE, 305, Short.MAX_VALUE)
                .addContainerGap())
        );
        pack();
    }// </editor-fold>//GEN-END:initComponents
    
    /**
     * Handles image list selection events. Initiate the
     * retrieval of a larger image for the selected thumbnail.
     */
    private void listImagesValueChanged(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_listImagesValueChanged
        // don't do anything if this is just a changing list
        // being populated by thumbnail images
        if (evt.getValueIsAdjusting() || listImages.isSelectionEmpty()) {
            return;
        }
        // handle selection made by the user
        int selectedIndex = listImages.getSelectedIndex();
        if (selectedIndex >= 0) {
            ImageInfo info = (ImageInfo) listImages.getSelectedValue();
            String id = info.getId();
            String server = info.getServer();
            String secret = info.getSecret();
            // no need to search an invalid thumbnail image
            if (id == null || server == null || secret == null) {
                return;
            }
            String strImageUrl = String.format(IMAGE_URL_FORMAT,
                    server, id, secret);
            retrieveImage(strImageUrl);
        }
    }//GEN-LAST:event_listImagesValueChanged
    
    private void retrieveImage(String imageUrl) {
        // SwingWorker objects can't be reused, so
        // create a new one as needed.
        ImageRetriever imgRetriever = new ImageRetriever(lblImage, imageUrl);
        progressSelectedImage.setValue(0);
        // listen for changes in the "progress" property
        // we can reuse the listener even though the worker thread
        // will be a new SwingWorker
        imgRetriever.addPropertyChangeListener(listenerSelectedImage);
        
        progressSelectedImage.setIndeterminate(true);
        
        // tell the worker thread to begin with this asynchronous method
        imgRetriever.execute();
        // this event thread continues immediately here without blocking
        
    }
    
    
    /**
     * Begin searching for images with search text in either the
     * title or description.
     */
    private void txtSearchKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_txtSearchKeyPressed
        if (evt.getKeyCode() == KeyEvent.VK_ENTER) {
            String strSearchText = txtSearch.getText();
            if (strSearchText != null && strSearchText.length() > 0) {
                searchImages(strSearchText, 1);
            }
        }
    }//GEN-LAST:event_txtSearchKeyPressed
    
    /**
     * searchImages
     * Initiate a search to retrieve a page of matched images.
     */
    private void searchImages(String strSearchText, int page) {
        if (searcher != null && !searcher.isDone()) {
            // cancel current search to begin a new one
            // only want one image search at a time.
            searcher.cancel(true);
            searcher = null;
        }
        
        // any parameters on the url GET command needs to URL-encoded
        String strEncodedText = null;
        try {
            strEncodedText = URLEncoder.encode(strSearchText, "UTF-8");
        } catch (UnsupportedEncodingException ex) {
            ex.printStackTrace();
            strEncodedText = strSearchText;
        }
        
        // remove matched images from any previous search
        listModel = new DefaultListModel();
        listImages.setModel(listModel);
        // clear up any displayed images in preparation to show another
        // selection from the image list
        lblImage.setIcon(null);
        lblImage.setText(null);
        // provide the list model so that the ImageSearcher can publish
        // images to the list immediately as they are available.
        searcher = new ImageSearcher(listModel, API_KEY, strEncodedText, page);
        searcher.addPropertyChangeListener(listenerMatchedImages);
        
        progressMatchedImages.setIndeterminate(true);
        
        // start the search!
        searcher.execute();
        // this event thread continues immediately here without blocking
    }
    
    /**
     * Create the MainFrame from the Swing
     * event dispatch thread...not this initial thread
     * 
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        API_KEY = getApiKey();
        if (API_KEY == null) {
            System.err.println("You must create a FlickrKey.properties file.");
            System.exit(-1);
        }
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                try {
                    UIManager.setLookAndFeel(
                            UIManager.getSystemLookAndFeelClassName());
                } catch (Exception ignore) {
                }
                
                new MainFrame().setVisible(true);
            }
        });
    }
    
    private static String getApiKey() {
//        InputStream is = null;
//        Properties props = null;
//        String key = null;
//        try {
//            is = MainFrame.class.getResourceAsStream("/FlickrKey.properties");
//            props = new Properties();
//            props.load(is);
//            key =  props.getProperty("API_KEY");
//        } catch (FileNotFoundException ex) {
//            System.err.println("The FlickrKey.properties file could not be found.");
//        } catch (IOException ioe) {
//            System.err.println("There was a problem reading the API_KEY property");
//        } finally {
//            try {
//                is.close();
//            } catch(Exception e) {}
//        }
//        return key;
        
        return "339db1433e5f6f11f3ad54135e6c07a9";

    }
    
    
    /**
     * ProgressListener listens to "progress" property
     * changes in the SwingWorkers that search and load
     * images.
     */
    class ProgressListener implements PropertyChangeListener {
        // prevent creation without providing a progress bar
        private ProgressListener() {}
        
        ProgressListener(JProgressBar progressBar) {
            this.progressBar = progressBar;
            this.progressBar.setValue(0);
        }
        
        public void propertyChange(PropertyChangeEvent evt) {
            String strPropertyName = evt.getPropertyName();
            if ("progress".equals(strPropertyName)) {
                progressBar.setIndeterminate(false);
                int progress = (Integer)evt.getNewValue();
                progressBar.setValue(progress);
            }
        }
        
        private JProgressBar progressBar;
    }
    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JLabel lblImage;
    private javax.swing.JLabel lblImageList;
    private javax.swing.JLabel lblSearch;
    private javax.swing.JLabel lblSelectedImage;
    private javax.swing.JList listImages;
    private javax.swing.JProgressBar progressMatchedImages;
    private javax.swing.JProgressBar progressSelectedImage;
    private javax.swing.JScrollPane scrollImageList;
    private javax.swing.JTextField txtSearch;
    // End of variables declaration//GEN-END:variables
    private DefaultListModel listModel;
    private ImageInfoRenderer renderer;
    private SwingWorker searcher;
    private ProgressListener listenerMatchedImages;
    private ProgressListener listenerSelectedImage;
    
    final private String IMAGE_URL_FORMAT = "http://static.flickr.com/%s/%s_%s.jpg";
    static private String API_KEY;
}
